package au.edu.qut.bpm.genbinder.genetic.mutation;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.uncommons.maths.number.ConstantGenerator;
import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;

import au.edu.qut.bpm.genbinder.genetic.genesis.LogAbstractFactory;
import au.edu.qut.bpm.genbinder.genetic.other.LogRepairer;
import au.edu.qut.bpm.genbinder.models.Activity;
import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;
import au.edu.qut.bpm.genbinder.models.Resource;

public class LogResourceMutationUnsafeRepaired implements EvolutionaryOperator<ConcreteEventLog> {

	private final NumberGenerator<Integer> mutationCaseCountVariable;
	private final NumberGenerator<Integer> mutationActivityCountVariable;

	public LogResourceMutationUnsafeRepaired() {
		this(5, 15);
	}

	public LogResourceMutationUnsafeRepaired(int mutationCaseCount, int mutationActivityCount) {
		this(new ConstantGenerator<Integer>(mutationCaseCount), new ConstantGenerator<Integer>(mutationActivityCount));
	}

	public LogResourceMutationUnsafeRepaired(NumberGenerator<Integer> mutationCaseCount, NumberGenerator<Integer> mutationActivityCount) {
		this.mutationCaseCountVariable = mutationCaseCount;
		this.mutationActivityCountVariable = mutationActivityCount;
		
	}

	public List<ConcreteEventLog> apply(List<ConcreteEventLog> selectedCandidates, Random rng) {
		List<ConcreteEventLog> mutatedCandidates = new ArrayList<ConcreteEventLog>(selectedCandidates.size());
		for (ConcreteEventLog log : selectedCandidates)
			mutatedCandidates.add(mutate(log, rng));
		return mutatedCandidates;
	}

	private ConcreteEventLog mutate(ConcreteEventLog log, Random rng) {
		ConcreteEventLog newLog = log.clone();

		int mutationCaseCount = Math.abs(mutationCaseCountVariable.nextValue());
		while (mutationCaseCount > 0) {
			int mutationActivityCount = Math.abs(mutationActivityCountVariable.nextValue());
			int c = rng.nextInt(newLog.getAbstractLog().getCases().size());
			
			while (mutationActivityCount > 0) {
				int a = rng.nextInt(newLog.getAbstractLog().getCases().get(c).getOrderedTrace().size());
				Activity act = newLog.getAbstractLog().getCases().get(c).getOrderedTrace().get(a);
				
				Set<Resource> newSet = null;
				newSet = LogAbstractFactory.getRandomResourceSet(newLog, act, rng);
				
				if (newSet != null)
					newLog.setActivityResources(act, newSet);
				--mutationActivityCount;
			}
			--mutationCaseCount;
		}
		
		if (!newLog.isLogSafe()) {
			List<ConcreteEventLog> result = new ArrayList<ConcreteEventLog>(1);
			result.add(newLog);

			LogRepairer repairer = new LogRepairer();
			result = repairer.apply(result, new Random());
			return result.get(0);
		}
		
		return newLog;
	}

	

}
